package top.cardone.context.util;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import lombok.extern.log4j.Log4j2;

import java.util.Map;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author yao hai tao
 * @date 2016/8/20
 */
@Log4j2
public class TableUtils {
    public static <R, C, V> Table<R, C, V> newTable(Map<String, Object> tableMap) {
        Table<R, C, V> table = HashBasedTable.create();

        if (tableMap.isEmpty()) {
            return table;
        }

        for (Map.Entry<String, Object> rowMapEntry : tableMap.entrySet()) {
            if (!(rowMapEntry.getValue() instanceof Map)) {
                continue;
            }

            Map<String, Object> rowMap = (Map<String, Object>) rowMapEntry.getValue();

            if (MapUtils.isEmpty(rowMap)) {
                continue;
            }

            for (Map.Entry<String, Object> colMapEntry : rowMap.entrySet()) {
                table.put((R) rowMapEntry.getKey(), (C) colMapEntry.getKey(), (V) colMapEntry.getValue());
            }
        }

        return table;
    }

    public static <R, C, V> Table<R, C, V> convert(Map<R, Map<C, V>> tableMap) {
        Table<R, C, V> table = HashBasedTable.create();

        if (tableMap.isEmpty()) {
            return table;
        }

        for (Map.Entry<R, Map<C, V>> rowMapEntry : tableMap.entrySet()) {
            if (MapUtils.isEmpty(rowMapEntry.getValue())) {
                continue;
            }

            for (Map.Entry<C, V> colMapEntry : rowMapEntry.getValue().entrySet()) {
                table.put(rowMapEntry.getKey(), colMapEntry.getKey(), colMapEntry.getValue());
            }
        }

        return table;
    }

    static Table<Object, Object, LongAdder> longAdderTable = Tables.newCustomTable(Maps.newConcurrentMap(),
            () -> Maps.newConcurrentMap());

    private static Lock lock = new ReentrantLock();

    public static Long longAdderIncrementGetSum(Object rowKey, Object columnKey) {
        if (longAdderTable.row(rowKey).size() > 10000) {
            longAdderTable.row(rowKey).clear();
        }

        LongAdder longAdder = longAdderTable.get(rowKey, columnKey);

        if (longAdder == null) {
            lock.lock();

            longAdder = longAdderTable.get(rowKey, columnKey);

            if (longAdder == null) {
                try {
                    longAdder = new LongAdder();

                    longAdderTable.put(rowKey, columnKey, longAdder);
                } finally {
                    lock.unlock();
                }
            }
        }

        longAdder.increment();

        return longAdder.sum();
    }
}